﻿using System.IO;
using System.Threading.Tasks;

namespace FastDFS.Client
{
    public class StorageClientEx : StorageClient
    {
        private const string SplitGroupNameAndFilenameSeperator = "/";
        public StorageClientEx()
            : base()
        {

        }

        public StorageClientEx(TrackerServer trackerServer, StorageServer storageServer)
            : base(trackerServer, storageServer)
        {

        }

        public static byte SplitFileId(string fileId, string[] results)
        {
            int pos = fileId.IndexOf(SplitGroupNameAndFilenameSeperator);
            if ((pos <= 0) || (pos == fileId.Length - 1))
            {
                throw new FdfsException("split file id error");
            }

            results[0] = fileId.Substring(0, pos); //group name
            results[1] = fileId.Substring(pos + 1); //file name
            return 0;
        }

        /// <summary>
        /// upload file to storage server (by file name)
        /// </summary>
        /// <param name="localFilename">local filename to upload</param>
        /// <param name="fileExtName">file ext name, do not include dot(.), null to extract ext name from the local filename</param>
        /// <param name="metaList">meta info array</param>
        /// <returns>file id(including group name and filename) if success</returns>
        public async Task<string> UploadFileExAsync(string localFilename, string fileExtName, NameValuePair[] metaList)
        {
            string[] parts = await this.UploadFileAsync(localFilename, fileExtName, metaList);
            return parts[0] + SplitGroupNameAndFilenameSeperator + parts[1];
        }

        /// <summary>
        /// upload file to storage server (by file name)
        /// </summary>
        /// <param name="groupName"></param>
        /// <param name="localFilename"></param>
        /// <param name="fileExtName"></param>
        /// <param name="metaList"></param>
        /// <returns></returns>
        public async Task<string> UploadFileExAsync(string groupName, string localFilename, string fileExtName,
            NameValuePair[] metaList)
        {
            string[] parts = await this.UploadFileAsync(groupName, localFilename, fileExtName, metaList);
            return parts[0] + SplitGroupNameAndFilenameSeperator + parts[1];
        }

        /// <summary>
        /// upload file to storage server (by file buff)
        /// </summary>
        /// <param name="fileBuff"></param>
        /// <param name="fileExtName"></param>
        /// <param name="metaList"></param>
        /// <returns></returns>
        public async Task<string> UploadFileExAsync(byte[] fileBuff, string fileExtName, NameValuePair[] metaList)
        {
            string[] parts = await this.UploadFileAsync(fileBuff, fileExtName, metaList);
            return parts[0] + SplitGroupNameAndFilenameSeperator + parts[1];
        }

        /// <summary>
        /// upload file to storage server (by file buff)
        /// </summary>
        /// <param name="groupName"></param>
        /// <param name="fileBuff"></param>
        /// <param name="fileExtName"></param>
        /// <param name="metaList"></param>
        /// <returns></returns>
        public async Task<string> UploadFileExAsync(string groupName, byte[] fileBuff, string fileExtName,
            NameValuePair[] metaList)
        {
            string[] parts = await this.UploadFileAsync(groupName, fileBuff, fileExtName, metaList);
            return parts[0] + SplitGroupNameAndFilenameSeperator + parts[1];
        }

        public async Task<string> UploadFileExAsync(string groupName, long fileSize,
            IUploadCallback callback, string fileExtName,
            NameValuePair[] metaList)
        {
            string[] parts = await this.UploadFileAsync(groupName, fileSize, callback, fileExtName, metaList);
            return parts[0] + SplitGroupNameAndFilenameSeperator + parts[1];
        }

        public async Task<string> UploadAppenderFileExAsync(string localFilename, string fileExtName,
            NameValuePair[] metaList)
        {
            string[] parts = await this.UploadAppenderFileAsync(localFilename, fileExtName, metaList);
            return parts[0] + SplitGroupNameAndFilenameSeperator + parts[1];
        }

        public async Task<string> UploadAppenderFileExAsync(string groupName, string localFilename, string fileExtName,
            NameValuePair[] metaList)
        {
            string[] parts = await this.UploadAppenderFileAsync(groupName, localFilename, fileExtName, metaList);
            return parts[0] + SplitGroupNameAndFilenameSeperator + parts[1];
        }

        public async Task<string> UploadAppenderFileExAsync(byte[] fileBuff, string fileExtName,
            NameValuePair[] metaList)
        {
            string[] parts = await this.UploadAppenderFileAsync(fileBuff, fileExtName, metaList);
            return parts[0] + SplitGroupNameAndFilenameSeperator + parts[1];
        }

        public async Task<string> UploadAppenderFileExAsync(string groupName, byte[] fileBuff, string fileExtName,
            NameValuePair[] metaList)
        {
            string[] parts = await this.UploadAppenderFileAsync(groupName, fileBuff, fileExtName, metaList);
            return parts[0] + SplitGroupNameAndFilenameSeperator + parts[1];
        }

        public async Task<string> UploadAppenderFileExAsync(string groupName, long fileSize,
            IUploadCallback callback, string fileExtName,
            NameValuePair[] metaList)
        {
            string[] parts = await this.UploadAppenderFileAsync(groupName, fileSize, callback, fileExtName, metaList);
            return parts[0] + SplitGroupNameAndFilenameSeperator + parts[1];
        }

        public async Task<string> UploadFileExAsync(string masterFileId, string prefixName,
            string localFilename, string fileExtName, NameValuePair[] metaList)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(masterFileId, parts);
            parts = await this.UploadFileAsync(parts[0], parts[1], prefixName,
                localFilename, fileExtName, metaList);
            return parts[0] + SplitGroupNameAndFilenameSeperator + parts[1];
        }


        public async Task<string> UploadFileExAsync(string masterFileId, string prefixName,
            Stream fileStream, string fileExtName, NameValuePair[] metaList)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(masterFileId, parts);
            parts = await this.UploadFileAsync(parts[0], parts[1], prefixName,
                fileStream, fileExtName, metaList);

            return parts[0] + SplitGroupNameAndFilenameSeperator + parts[1];
        }


        public async Task<string> UploadFileExAsync(string masterFileId, string prefixName,
            byte[] fileBuff, string fileExtName, NameValuePair[] metaList)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(masterFileId, parts);

            parts = await this.UploadFileAsync(parts[0], parts[1], prefixName, fileBuff, fileExtName, metaList);
            return parts[0] + SplitGroupNameAndFilenameSeperator + parts[1];
        }

        public async Task<string> UploadFileExAsync(string masterFileId, string prefixName,
            byte[] fileBuff, int offset, int length, string fileExtName,
            NameValuePair[] metaList)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(masterFileId, parts);

            parts = await this.UploadFileAsync(parts[0], parts[1], prefixName, fileBuff,
                offset, length, fileExtName, metaList);
            return parts[0] + SplitGroupNameAndFilenameSeperator + parts[1];
        }

        public async Task<string> UploadFileExAsync(string masterFileId, string prefixName, long fileSize,
            IUploadCallback callback, string fileExtName,
            NameValuePair[] metaList)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(masterFileId, parts);

            parts = await this.UploadFileAsync(parts[0], parts[1], prefixName, fileSize, callback, fileExtName, metaList);
            return parts[0] + SplitGroupNameAndFilenameSeperator + parts[1];
        }

        public async Task<int> AppendFileExAsync(string appenderFileId, string localFilename)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(appenderFileId, parts);

            return await this.AppendFileAsync(parts[0], parts[1], localFilename);
        }

        public async Task<int> AppendFileExAsync(string appenderFileId, byte[] fileBuff)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(appenderFileId, parts);

            return await this.AppendFileAsync(parts[0], parts[1], fileBuff);
        }

        public async Task<int> AppendFileExAsync(string appenderFileId, byte[] fileBuff, int offset, int length)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(appenderFileId, parts);

            return await this.AppendFileAsync(parts[0], parts[1], fileBuff, offset, length);
        }

        public async Task<int> AppendFileExAsync(string appenderFileId, long fileSize, IUploadCallback callback)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(appenderFileId, parts);

            return await this.AppendFileAsync(parts[0], parts[1], fileSize, callback);
        }

        public async Task<int> ModifyFileExAsync(string appenderFileId,
            long fileOffset, string localFilename)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(appenderFileId, parts);

            return await this.ModifyFileAsync(parts[0], parts[1], fileOffset, localFilename);
        }

        public async Task<int> ModifyFileExAsync(string appenderFileId,
            long fileOffset, byte[] fileBuff)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(appenderFileId, parts);

            return await this.ModifyFileAsync(parts[0], parts[1], fileOffset, fileBuff);
        }

        public async Task<int> ModifyFileExAsync(string appenderFileId,
            long fileOffset, byte[] fileBuff, int bufferOffset, int bufferLength)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(appenderFileId, parts);

            return await ModifyFileAsync(parts[0], parts[1], fileOffset,
                fileBuff, bufferOffset, bufferLength);
        }

        public async Task<int> ModifyFileExAsync(string appenderFileId,
            long fileOffset, long modifySize, IUploadCallback callback)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(appenderFileId, parts);

            return await this.ModifyFileAsync(parts[0], parts[1], fileOffset, modifySize, callback);
        }

        public async Task<int> DeleteFileExAsync(string fileId)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(fileId, parts);

            return await this.DeleteFileAsync(parts[0], parts[1]);
        }

        public async Task<int> TruncateFileExAsync(string appenderFileId)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(appenderFileId, parts);

            return await this.TruncateFileAsync(parts[0], parts[1]);
        }

        public async Task<int> TruncateFileExAsync(string appenderFileId, long truncatedFileSize)
        {
            string[] parts = new string[2];

            return await this.TruncateFileAsync(parts[0], parts[1], truncatedFileSize);
        }

        public async Task<byte[]> DownloadFileExAsync(string fileId)
        {
            long fileOffset = 0;
            long downloadBytes = 0;

            return await this.DownloadFileExAsync(fileId, fileOffset, downloadBytes);
        }

        public async Task<byte[]> DownloadFileExAsync(string fileId, long fileOffset, long downloadBytes)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(fileId, parts);

            return await this.DownloadFileAsync(parts[0], parts[1], fileOffset, downloadBytes);
        }

        public async Task<int> DownloadFileExAsync(string fileId, string localFilename)
        {
            long fileOffset = 0;
            long downloadBytes = 0;

            return await this.DownloadFileExAsync(fileId, fileOffset, downloadBytes, localFilename);
        }

        public async Task<int> DownloadFileExAsync(string fileId, long fileOffset, long downloadBytes, string localFilename)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(fileId, parts);

            return await this.DownloadFileAsync(parts[0], parts[1], fileOffset, downloadBytes, localFilename);
        }

        public async Task<int> DownloadFileExAsync(string fileId, IDownloadCallback callback)
        {
            long fileOffset = 0;
            long downloadBytes = 0;

            return await this.DownloadFileExAsync(fileId, fileOffset, downloadBytes, callback);
        }

        public async Task<int> DownloadFileExAsync(string fileId, long fileOffset, long downloadBytes, IDownloadCallback callback)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(fileId, parts);

            return await this.DownloadFileAsync(parts[0], parts[1], fileOffset, downloadBytes, callback);
        }

        public async Task<NameValuePair[]> GetMetadataExAsync(string fileId)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(fileId, parts);

            return await this.GetMetadataAsync(parts[0], parts[1]);
        }

        public async Task<int> SetMetadataAsync(string fileId, NameValuePair[] metaList, byte opFlag)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(fileId, parts);

            return await this.SetMetadataAsync(parts[0], parts[1], metaList, opFlag);
        }

        public async Task<FileInfo> QueryFileInfoExAsync(string fileId)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(fileId, parts);

            return await this.QueryFileInfoAsync(parts[0], parts[1]);
        }

        public async Task<FileInfo> GetFileInfoAsync(string fileId)
        {
            string[] parts = new string[2];
            this.Errno = SplitFileId(fileId, parts);

            return await this.GetFileInfoAsync(parts[0], parts[1]);
        }
    }
}
